use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc, Mutex,
};

use boardinfo::{boardinfo, BoardInfo};
use device::device_for_each;
use dfs::{
    inode::{link_path_walk, DfsInode, DirCreateOptions},
    params::{ParamCreateOptions, ParamOperation},
};
use manage::{ManageGuestStatus, SiminkManage};

struct CurrentBoard {
    board: Mutex<String>,
    manage: Arc<SiminkManage>,
}

impl CurrentBoard {
    fn create(manage: Arc<SiminkManage>) -> Self {
        Self { board: Mutex::new(String::new()), manage }
    }
}

pub(crate) fn current_board_init(manage: Arc<SiminkManage>, boardinode: Arc<DfsInode>) {
    ParamCreateOptions::new()
        .read_write(true)
        .name("current_board")
        .create_param_file(boardinode, Arc::new(CurrentBoard::create(manage)))
        .unwrap();
}

impl ParamOperation for CurrentBoard {
    fn read(&self) -> String {
        let key = self.board.lock().unwrap();
        key.clone()
    }

    fn wrtie(&self, buf: &str) -> monitor::MonResult<()> {
        let boardinfo = boardinfo(buf);
        if boardinfo.is_none() {
            return Err(monitor::MonError::NoExistObj);
        }

        let mut lock = self.board.lock().unwrap();
        if !lock.is_empty() {
            return Err(monitor::MonError::PermOperation);
        }
        *lock = String::from(buf);

        let board = boardinfo.unwrap();
        let board_inode = link_path_walk("/board").unwrap();
        let pre_config_inode = DirCreateOptions::new()
            .read_write(true)
            .name("pre_config")
            .create_dir(board_inode)
            .unwrap();

        ParamCreateOptions::new()
            .read_write(true)
            .name("done")
            .create_param_file(
                pre_config_inode.clone(),
                Arc::new(PreConfigEnd {
                    end: AtomicBool::new(false),
                    board: board.clone(),
                    manage: self.manage.clone(),
                }),
            )
            .unwrap();

        board.pre_config(pre_config_inode).unwrap();
        self.manage.set_guest_status(ManageGuestStatus::GuestPreConfig);

        Ok(())
    }
}

struct PreConfigEnd {
    end: AtomicBool,
    board: Arc<BoardInfo>,
    manage: Arc<SiminkManage>,
}

impl ParamOperation for PreConfigEnd {
    fn read(&self) -> String {
        self.end.load(Ordering::Relaxed).to_string()
    }

    fn wrtie(&self, buf: &str) -> dfs::DfsResult<()> {
        if !self.end.load(Ordering::Relaxed) {
            self.end.store(buf.parse().map_err(|_| dfs::DfsError::InvalidArgs)?, Ordering::Relaxed);
            self.board.pre_config_end(link_path_walk("/board/pre_config").unwrap())?;
            self.manage.set_guest_status(ManageGuestStatus::GuestConfig);

            self.board.create_board().unwrap();

            let board_inode = link_path_walk("/board").unwrap();
            let config_inode = DirCreateOptions::new()
                .read_write(true)
                .name("config")
                .create_dir(board_inode)
                .unwrap();

            ParamCreateOptions::new()
                .read_write(true)
                .name("done")
                .create_param_file(
                    config_inode.clone(),
                    Arc::new(ConfigEnd {
                        end: AtomicBool::new(false),
                        manage: self.manage.clone(),
                    }),
                )
                .unwrap();

            device_for_each(|dev| {
                dev.create_options(config_inode.clone()).unwrap();
            });
        }
        Ok(())
    }
}

struct ConfigEnd {
    end: AtomicBool,
    manage: Arc<SiminkManage>,
}

impl ParamOperation for ConfigEnd {
    fn read(&self) -> String {
        self.end.load(Ordering::Relaxed).to_string()
    }

    fn wrtie(&self, buf: &str) -> dfs::DfsResult<()> {
        if !self.end.load(Ordering::Relaxed) {
            self.end.store(buf.parse().map_err(|_| dfs::DfsError::InvalidArgs)?, Ordering::Relaxed);
            device_for_each(|dev| {
                dev.create_options_end(link_path_walk("/board/config").unwrap()).unwrap();
            });
            self.manage.set_guest_status(ManageGuestStatus::GuestConfiged);

            device_for_each(|dev| {
                dev.reset(device::DeviceResetType::Cold);
                dev.realize();
            });

            // TODO: 设备交互式文件夹
            // TODO: 设备资源文件夹
        }
        Ok(())
    }
}
